package com.cheng.servlet;


import com.cheng.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

//spring mvc 的servlet,注解或配置说明该类是servlet
@Controller//告诉程序该类是一个控制器,配置spring扫描包
/*@RequestMapping("test")为该servlet设置servlet路径名，通过路径名/类名来访问*/
@SessionAttributes(value = "ModelMap,welcome")//放进request域的同时放进session域中
//@SessionAttributes(types = Student.class)//通过类型放入
public class springMvcServlet {
    //web.xml拦截到请求后查找名为welcome的servlet



    @RequestMapping(value = "welcome/{name}")
    public String welcome0(@PathVariable("name") String name){
        System.out.println(name);
        return "city";
    }





    /*
    * rest风格，get post的使用场景
    * get： 查
    * post： 增
    * delete： 删
    * put：改
    * 但是普通浏览器没有delete和put，不能支持，需要处理
    * 通过过滤器，客户端发送请求，过滤器拦截，添加delete和put的支持，再交给程序处理，springmvc提供了过滤器web.xml中配置
    *
    * */

    @RequestMapping(value = "welcome",method = RequestMethod.POST,params = {"name","name!=zx","!ag"})//用于拦截请求，定位到servlet,method = RequestMethod.POST之拦截post请求
    //params = {"name=x"}指定请求携带参数name否者找不到或只能name=x
    public String welcome(){
        return "city";//设置返回值，跳转页面，city是页面名字，会去到springmvc.xml中的试图解释器中拼写全路径。然后跳转
        //直接return使用的是请求转发，地址栏不变，停留在servlet
    }

    @RequestMapping(value = "welcome2",headers = {"键值对"})//headers规定请求头，如果有人攻击可以通过请求头分析，然后响应的时候断掉（个人猜想）
    public String welcome2(){
        return "city";
    }


    /*ant风格的请求路径
    *    *代表任意个字符
    *    ？单个字符
    *    **任意目录 value="welcome3/s/s/s/test都能访问"
    * */
    @RequestMapping(value = "welcome3/**/test")//请求地址插入任意统配符
    public String welcome3(){
        return "city";
    }

    //取ui层传入的值，假设传了name=city只能通过超链接来传值这种方法在href="servle/值"地址栏上传值



/*测试过滤后delete和put的请求*/
    @RequestMapping(value = "testPost/{name}",method = RequestMethod.DELETE)
    public String testPost(@PathVariable("name") String name){
        System.out.println(name);
        //service进行dao层删操作
        return "city";
    }


    /*类中方法名不可以重名，但是@requestmapping注解value属性可以重载，即servlet可以重载，然后通过servlet的其他参数进行
    * 匹配，比如，同名的servlet但是一个是post方式，一个是set方式，请求的serlet不是同一个*/





    //获取input参数
    //在超链接传值通过地址的/值 来传值，后台通过@pathvariable来接收，同时form的地址也可以传值
    //from的input传值通过requestparam拿到
    @RequestMapping(value = "testcanshu")
    public String testcanshu(@RequestParam("test") String test,@RequestParam(value = "test1",required = false/*该值不是必须的*/,defaultValue = "24"/*默认值*/) String test1){
        System.out.println(test);
        System.out.println(test1);
        //service进行dao层删操作
        return "city";
    }


    /*获取头信息参数中使用@requestheader（键）拿到*/


    /*使用实体类接收数据*/
    @RequestMapping(value = "testClass")
    public String testClass(/*使用数据校验必须在该参数中加注解,校验该对象*/@Valid Student student,/*使用数据格式化的时候如果出现报错，可以使用该参数接收，保存的是前一个参数的错误信息，说明接收参数的和返回值的位置不能调换*/BindingResult result, Map<String,Object> map){//springmvc接受到对象自动保存到student，约定，input的name值必须与属性值相同
        System.out.println(student);
        //service进行dao层删操作
        if (result.getErrorCount()>0){
            for (FieldError fieldError : result.getFieldErrors()) {//快捷键，list.for
                System.out.println(fieldError.getDefaultMessage());
            }
            map.put("Errors",result.getFieldErrors());
        }
        return "city";
    }


    /*springmvc中使用原生态servlt 往方法参数中加入httpservletrequest和httpservletrespond参数*/



    //携带数据返回前台-----------------------------
    /*
    * 1.处理模型数据
        如果跳转时需要带数据：V、M,则可以使用以下方式：
        ModelAndView、ModelMap  、Map、Model   -数据放在了request作用域
        @SessionAttributes、@ModelAttribute
    * */
    @RequestMapping("modelAndView")
    public ModelAndView modelAndView(){
        //ModelAndView   返回的数据为视图和数据就是返回的string包含数据和跳转的页面
        ModelAndView mv=new ModelAndView("city");//views/city/.jsp
        Student student=new Student();
        student.setSNo(24);//注意看这里的setSNo，原来是sNo生成的set和get需要把首字母变大写，
        // el获取的方式是通过set拿的，所以拿的是SNO，而不是sNo
        mv.addObject("student",student);
        return mv;//放到request域里去了
    }

    //ModelMap  、Map、Model,都类似
    @RequestMapping(value = "ModelMap")
    public String ModelMap(ModelMap mm){
        //service进行dao层删操作
        Student student=new Student();
        student.setSNo(22);
        mm.put("student",student);
        return "city";//返回v层，数据单独返回
    }

//改数据的特殊操作
    @RequestMapping(value = "updata")
    public String updata(Student student,ModelMap mm){//约定该student变成了先查出来的student，然后再通过代码在完整的student中修改该类的属性，如果不先查，那修改完后再查数据显示的时候前端传来的信息不完整则显示不完全，如果先查则可以在这基础上改完再显示
        //service进行dao层删操作
        /*
        * 一旦模型中存在，这个参数的字段会被所有的名字匹配的请求参数所填充。这在 Spring MVC 中叫做数据绑定，它能够把你从要对每一个字段进行类型转换的繁重体力劳动中解救出来
        * */
        /*db修改数据，数据来源为修改后的student*/student.setSNo(student.getSNo());//前台来到该方法，先跳到修饰的方法，返回完整没修改前的student，再通过springmvc的数据绑定将前台获取的student将信息不完整的通过属性名匹配自动完成补入，形成了一个修改数据后的student，赋值给该servlet方法一个修改后的完整的student参数，获取修改后的数据后通过db往数据库中修改
        System.out.println(student);
        mm.put("s",student.getSName());
        return "city";//返回v层，数据单独返回
    }

    //先查询数据是否存在并拿到，再进行数据的修改springmvc通过自动匹配实现修改前先执行查的servlet的操作
    @ModelAttribute//任何一次请求前都会先执行一遍该注解修饰的方法，因此最好把更新单独写一个servvlet中
    public void queryStudentBySno(Map<String,Object> map){//根据前端传来的student中有一个存放该用户id的input隐藏域通过该隐藏域获取用户id
        //查到的数据
        Student student=new Student();
        student.setSNo(22);
        student.setSName("s");
        student.setSAge("25");
        map.put("student",student);//约定键名为修改servlet方法的参数的数据类型的首字母小写，然后自动讲查到的student传给修改的参数值
    }
    /*@ModelAttribute
    i.经常在 更新时使用
    ii.在不改变原有代码的基础之上，插入一个新方法。
    */
    //一个Servlet 对应一个功能：
    //增删改查  对应于 4个Servlet
    //更新：	Servlet  - SpringMVC的Controller


    @RequestMapping(value = "i18n")
    public String i18n(){
        return "city";
    }

    @RequestMapping("chongDingXing")
    public String chongDingXing(){
        //默认请求转发，地址栏不改变，如果需要改动为重定向，则需要在地址栏加上前缀后缀，只要加上两个说明，就需要写完整的前缀后缀
        //forward:请求转发（地址栏不变）    redirect:重定向（地址栏改变）
        //重定向和请求转发的使用场景
        //重定向与请求转发使用
        //前后两个页面 有数据传递 用请求转发，没有则用重定向。
        //比如servlet查询了数据需要在页面显示，就用请求转发。
        //比如servlet做了update操作跳转到其他页面，就用重定向。
        return "redirect:/views/city.jsp";
    }



    @ResponseBody//告诉SpringMVC，此时的返回 不是一个 View页面，而是一个 ajax调用的返回值（Json数组）
    @RequestMapping(value="testJson")
    public List<Student> testJson() {
        //Controller-Service-dao
        //StudentService studentService = new StudentServiceImp();
//			List<Student> students =  studentService.qeuryAllStudent();
        //模拟调用service的查询操作

        Student stu1 = new Student();
        Student stu2 = new Student();
        Student stu3 = new Student();
        stu1.setSNo(1);
        stu2.setSNo(2);
        stu3.setSNo(3);
        List<Student> students = new ArrayList<>();
        students.add(stu1) ;
        students.add(stu2) ;
        students.add(stu3) ;

        return students;
    }
}
